import { get as getCookie } from '@/utils/cookie';

const utokenCookieKey = 'u_login_token';

function buildParams(data: { [key: string]: any }): URLSearchParams {
  const formData = new URLSearchParams();
  for (const key in data) {
    const val = data[key];
    if (!Array.isArray(val)) {
      formData.append(key, String(val));
    } else {
      val.forEach((d: any) => {
        formData.append(key + '[]', String(d));
      });
    }
  }
  return formData;
}

export default class ApiClient {
  #apiHost: string;
  #respFilter?: (apiResp: any, path: string) => any;
  #withToken: boolean;
  #withCookie: boolean;

  constructor(apiHost: string, opts: { withToken?: boolean; withCookie?: boolean } = { withToken: false, withCookie: false }) {
    this.#apiHost = apiHost;
    this.#withToken = opts.withToken ?? false;
    this.#withCookie = opts.withCookie ?? false;
  }

  regRespFilter(func: ((apiResp: any, path: string) => any) | null): void {
    if (typeof func === 'function') {
      this.#respFilter = func.bind(this);
    }
  }

   async requestWithFilter(url: string, opts: any): Promise<any> {
    opts = {
      withToken: this.#withToken,
      credentials: this.#withCookie ? 'include' : 'omit',
      ...opts,
    };
    if (!/^https?:\/\//.test(url)) {
      url = window.location.origin + url;
    }
    const apiResp = await ApiClient.requestWithToken(url, opts);
    if (this.#respFilter) {
      return this.#respFilter(apiResp, new URL(url).pathname);
    }
    return apiResp;
  }

  get(path: string, qs: { [key: string]: any } = {}, opts: any = {}): Promise<any> {
    qs = qs || {};
    const query = buildParams(qs).toString();
    const url = this.#apiHost + path + (query ? `?${query}` : '');
    return this.requestWithFilter(url, opts);
  }

  post(path: string, body: any = false, opts: any = {}): Promise<any> {
    const url = this.#apiHost + path;
    opts = {
      json: false,
      headers: {},
      method: 'POST',
      ...opts,
    };
    if (body) {
      if (opts.json) {
        opts.headers = Object.assign(opts.headers, {
          'Content-Type': 'application/json',
        });
        opts.body = JSON.stringify(body);
      } else if (!opts.headers?.['Content-Type']) {
        opts.headers = Object.assign(opts.headers, {
          'Content-Type': 'application/x-www-form-urlencoded',
        });
        opts.body = buildParams(body);
      }
    }

    delete opts.json;
    return this.requestWithFilter(url, opts);
  }

  static async request(url: string, opts: any = {}): Promise<any> {
    const resp = await fetch(url, {
      mode: 'cors',
      ...opts,
       credentials: 'include'
    });

    if (!resp.ok) {
      throw new Error(`Request "${resp.url}" got error with status: [${resp.status}] ${resp.statusText}`
      );
    } else {
      const respContentType = resp.headers.get('Content-Type');
      if (/^application\/json/i.test(respContentType!)) {
        return resp.json();
      } else if (respContentType === 'application/octet-stream') {
        const file = resp.headers.get('Content-Disposition');
        let filename = '';
        if (file) {
          file.split(';').forEach((item) => {
            if (item.trim().startsWith('filename')) {
              filename = item.split('=')[1]?.trim()?.replace(/['"]/g, '') ?? '';
            }
          });
        }

        return {
          filename,
          blob: await resp.blob(),
        };
      }
      return resp.text();
    }
  }

  static async requestWithToken(url: string, opts: { withToken?: boolean; headers?: { [key: string]: string }; credentials?: string } = { withToken: false }): Promise<any> {
    opts.headers = opts.headers || {};
    const token = getCookie(utokenCookieKey);
    
    if (token && opts.withToken) {
      
      opts.headers = Object.assign(opts.headers, { 'Authorization': `Zoro token=${token}` });
      opts.credentials = 'include';
    }
    delete opts.withToken;
    console.log(opts);
    
    return this.request(url, opts);
  }

  static getToken(): string | null {
    return getCookie(utokenCookieKey);
  }
}

export const jsonp = (url: string, opts?: Record<string, any>) => {
  return new Promise<unknown>((resolve, reject) => {
    const callbackName = `jsonp_${Date.now()}`;
    const script: HTMLScriptElement = document.createElement('script');
    script.src = `${url}${url.includes('?') ? '&' : '?'}callback=${callbackName}`;
    document.body.appendChild(script);

    // 使用 any 是因为 JSONP 回调函数的数据类型可能不同
    (window as any)[callbackName] = (data: any) => {
      resolve(data);
      document.body.removeChild(script);
      delete (window as any)[callbackName];
    };

    script.onerror = (err: any) => {
      reject(err);
      document.body.removeChild(script);
      delete (window as any)[callbackName];
    };
  });
};
